package com.vision.sales.sv.serv;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import com.vision.core.cm.db.data.Company;
import com.vision.core.cm.exception.RpcException;
import com.vision.core.sv.db.CustomSessionFactoryBean;
import com.vision.core.sv.db.mgr.DataManager;
import com.vision.core.sv.db.mgr.ISimpleCriteria;
import com.vision.core.sv.db.mgr.SimpleCriteria;
import com.vision.core.sv.serv.BaseRpcServlet;
import com.vision.core.sv.serv.util.ServletUtils;
import com.vision.sales.cm.data.SalesPermissions;
import com.vision.sales.cm.db.data.Customer;
import com.vision.sales.cm.db.data.DeliveryReceipt;
import com.vision.sales.cm.db.data.SalesInvoice;
import com.vision.sales.sv.db.mgr.DeliveryReceiptManager;
import com.vision.sales.sv.report.AbstractDataSource;
import com.vision.sales.sv.report.DeliveryReceiptRow;
import com.vision.sales.sv.report.Report;
import com.vision.sales.sv.report.ReportWriter;

/**
 * 
 * @author Mark
 *
 */
public class PrintDeliveryReceiptsReportServlet extends BaseRpcServlet {

	private static final long serialVersionUID = 1L;
	
	private static final Logger log = LoggerFactory.getLogger(PrintDeliveryReceiptsReportServlet.class);
	
	@Autowired
	@Qualifier("deliveryReceiptManager")
	private DataManager<DeliveryReceipt> deliveryReceiptManager;


	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			ServletUtils.checkPermissions(SalesPermissions.ViewOutstandingReceivablesReport);
		} catch (RpcException e) {
			resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You are not authorized to perform this operation.");
			return;
		} 
		
		// Parameters --
		log.debug("Parsing parameters from URL...");
		DeliveryReceiptParams params = new DeliveryReceiptParams(req);
		if (params.error != null) {
			resp.sendError(HttpServletResponse.SC_BAD_REQUEST, params.error);
			return;
		}
		
		try {
			DateFormat fnameDateFormat = new SimpleDateFormat("yyyyMMdd");
			DateFormat titleDateFormat = new SimpleDateFormat("MMM d, yyyy");
			
			// Build the Report
			log.debug("Building report...");
			Report report = new Report();
			report.setSize(params.size);
			report.setOrientation(params.orientation);

			report.setFilename(params.title + (params.hideInvoiced ? "_wo_Inv" : "") 
					+ "_" + fnameDateFormat.format(params.startDate)
					+ "_" + fnameDateFormat.format(params.endDate));
			
			report.setTitle(params.title + (params.hideInvoiced ? " with no Sales Invoice" : ""));
			report.setSubtitle(titleDateFormat.format(params.startDate) + " - " + 
								titleDateFormat.format(params.endDate));

			// Columns and Grouping
			report.addDateColumn("date", "Date", 20, "MM/dd/yyyy");
			report.addStringColumn("customer", "Customer", 40, null);
			report.addStringColumn("drNo", "D/R No.", 20, null);
			if (!params.hideInvoiced){
				report.addStringColumn("invoice", "Invoice No.", 20, null);
			}
			
			// Data Source
			log.debug("Creating data source...");
			report.setDataSource(getDataSource(params));
			
			// Print and Write to Stream
			log.debug("Writing report to stream...");
			ReportWriter.write(report, resp);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "The server was unable to process the request.");
		}
	}

	private JRDataSource getDataSource(DeliveryReceiptParams params) {
		List<DeliveryReceiptRow> rows = getDeliveryReceiptRows(params);
		
		JRDataSource ds = new AbstractDataSource<DeliveryReceiptRow>(rows) {
			@Override
			public Object getValue(DeliveryReceiptRow row, String fieldName) {
				return row.get(fieldName);
			}
		};
		return ds;
	}

	private List<DeliveryReceiptRow> getDeliveryReceiptRows(
			DeliveryReceiptParams params) {
		
		List<DeliveryReceiptRow> rows = new ArrayList<DeliveryReceiptRow>(); 
		List<DeliveryReceipt> deliveryReceipts = getDeliveryReceipts(params);
		for (DeliveryReceipt deliveryReceipt : deliveryReceipts) {
			rows.add(new DeliveryReceiptRow(deliveryReceipt));
		}
		return rows;
	}

	@SuppressWarnings("unchecked")
	private List<DeliveryReceipt> getDeliveryReceipts(DeliveryReceiptParams params) {

		// XXX @Autowired doesn't seem to work, thus this patch
		deliveryReceiptManager = new DeliveryReceiptManager();
		deliveryReceiptManager.setSessionFactory(CustomSessionFactoryBean.getFactory());
		
		ISimpleCriteria crit = SimpleCriteria.forClass(DeliveryReceipt.class);
		// not yet posted
		// crit.neq("status", TransactionStatus.POSTED);

		// filter D/Rs that has no invoice yet
		if (params.hideInvoiced){
			crit.isNull("salesInvoice");
		}
		
		// filter to include only the start and end date
		crit.between("date", params.startDate, params.endDate);
		
		if (params.companyIds != null && !params.companyIds.isEmpty())
			crit.in("company.id", params.companyIds);
		
		if (params.customerIds != null && !params.customerIds.isEmpty())
			crit.in("customer.id", params.customerIds);
		
		crit.desc("date");
		
		log.debug("Retrieving delivery receipts...");
		List<DeliveryReceipt> deliveryReceipts = deliveryReceiptManager.getTemplate().findByCriteria(crit.getDetached());
		return deliveryReceipts;
	}

	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	static class DeliveryReceiptParams extends SalesReportParams {
		
		boolean hideInvoiced = false;
		Date startDate;
		Date endDate;

		public DeliveryReceiptParams(HttpServletRequest req) {
			super(req);
			
			try {
				startDate = yyyyMMdd.parse(req.getParameter("start"));
			} catch (Exception e) {
				error = "Unable to determine start date.";
				return;
			}
			
			try {
				endDate = yyyyMMdd.parse(req.getParameter("end"));
			} catch (Exception e) {
				error = "Unable to determine end date.";
				return;
			}

			String isHideInvoiced = req.getParameter("hideInvoiced");
			if (isHideInvoiced != null) {
				try {
					this.hideInvoiced = Boolean.parseBoolean(isHideInvoiced);
				} catch (Exception e) {
				}
			}
		}
		
	}

	static class DeliveryReceiptDataSource extends JRBeanCollectionDataSource {
		
		Company company;
		Customer customer;
		Date startDate;
		Date endDate;

		public DeliveryReceiptDataSource(Company company, Customer customer, Date startDate, Date endDate, Collection<SalesInvoice> beanCollection) {
			super(beanCollection);
			
			this.company = company;
			this.customer = customer;
			this.startDate = startDate;
			this.endDate = endDate;
		}
		
		@Override
		public Object getFieldValue(Object bean, JRField field) throws JRException {
			String name = field.getName();
			
			if (name.startsWith("company.")) {
				name = name.replace("company.", "");
				return super.getBeanProperty(company, name);
			}
			
			if (name.startsWith("customer.")) {
				name = name.replace("customer.", "");
				return super.getBeanProperty(customer, name);
			}
			
			if ("startDate".equals(name)) {
				return startDate;
			}
			
			if ("endDate".equals(name)) {
				return endDate;
			}
			
			if (name.startsWith("invoice.")) {
				name = name.replace("invoice.", "");
				return super.getBeanProperty(bean, name);
			}
			
			return super.getFieldValue(bean, field);
		}
		
	}
	
}
